"""
数据获取服务模块
"""
import yfinance as yf
import pandas as pd
import numpy as np
import time
import random
from datetime import datetime, timedelta
from typing import Optional, Dict, Any, Tuple
from config import Config


class DataService:
    """数据获取服务类"""

    @staticmethod
    def retry_yfinance_call(func, max_retries: int = None, delay: int = None):
        """
        重试yfinance调用，处理429错误

        Args:
            func: 要执行的函数
            max_retries: 最大重试次数
            delay: 重试延迟基数

        Returns:
            函数执行结果
        """
        max_retries = max_retries or Config.YF_MAX_RETRIES
        delay = delay or Config.YF_RETRY_DELAY

        for attempt in range(max_retries):
            try:
                result = func()
                return result
            except Exception as e:
                if "429" in str(e) and attempt < max_retries - 1:
                    # 指数退避 + 随机延迟
                    sleep_time = delay * (2 ** attempt) + random.uniform(0, 1)
                    print(f"遇到429错误，{sleep_time:.1f}秒后重试 (尝试 {attempt + 1}/{max_retries})")
                    time.sleep(sleep_time)
                else:
                    raise e
        return None

    @classmethod
    def get_stock_info(cls, symbol: str) -> Dict[str, Any]:
        """
        获取股票基本信息

        Args:
            symbol: 股票代码

        Returns:
            股票基本信息字典
        """
        def get_stock_data():
            stock = yf.Ticker(symbol)
            return stock.info

        info = cls.retry_yfinance_call(get_stock_data)
        if info is None:
            raise Exception('Failed to fetch stock data after multiple retries')

        return info

    @classmethod
    def get_historical_data(cls, symbol: str, days: int = 365) -> pd.DataFrame:
        """
        获取历史数据

        Args:
            symbol: 股票代码
            days: 历史数据天数

        Returns:
            历史数据DataFrame
        """
        end_date = datetime.now()
        start_date = end_date - timedelta(days=days)

        def get_data():
            return yf.download(symbol, start=start_date, end=end_date, auto_adjust=False)

        data = cls.retry_yfinance_call(get_data)
        if data is None:
            raise Exception('Failed to fetch historical data after multiple retries')

        return data

    @classmethod
    def get_financial_data(cls, symbol: str) -> Tuple[Dict[str, Any], pd.DataFrame]:
        """
        获取完整的股票数据（基本信息+历史数据）

        Args:
            symbol: 股票代码

        Returns:
            (基本信息, 历史数据) 元组
        """
        def get_all_data():
            stock = yf.Ticker(symbol)
            info = stock.info

            # 获取历史数据用于计算技术指标
            end_date = datetime.now()
            start_date = end_date - timedelta(days=365)
            stock_data = yf.download(symbol, start=start_date, end=end_date, auto_adjust=False)

            return info, stock_data

        info, stock_data = cls.retry_yfinance_call(get_all_data)
        if info is None or stock_data is None:
            raise Exception('Failed to fetch data after multiple retries')

        return info, stock_data

    @staticmethod
    def safe_float(value: Any, default: float = 0) -> float:
        """
        安全地将值转换为浮点数

        Args:
            value: 要转换的值
            default: 默认值

        Returns:
            转换后的浮点数
        """
        if value is None:
            return default
        try:
            if hasattr(value, 'iloc'):
                return float(value.iloc[0]) if len(value) > 0 else default
            return float(value)
        except (ValueError, TypeError, AttributeError):
            return default

    @staticmethod
    def safe_string(value: Any, default: str = '') -> str:
        """
        安全地将值转换为字符串

        Args:
            value: 要转换的值
            default: 默认值

        Returns:
            转换后的字符串
        """
        if value is None:
            return default
        try:
            if hasattr(value, 'iloc'):
                return str(value.iloc[0]) if len(value) > 0 else default
            return str(value)
        except (ValueError, TypeError, AttributeError):
            return default